package pl.project.engineering.services;

import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import javax.naming.ConfigurationException;
import lombok.Delegate;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.tapestry5.ioc.annotations.EagerLoad;
import org.apache.tapestry5.ioc.annotations.InjectService;

/**
 *
 * @author ksmolnicki
 */
@Slf4j
@EagerLoad
public final class SerialComPort {

  @Getter @Setter
  private String portName;
  @Getter
  private boolean connected;
  private HashMap<String, CommPortIdentifier> portsIDMap;
  @Delegate
  private SerialPort serialPort;
  private SerialReader serialReader;

  public SerialComPort(@InjectService("AppConfig") AppConfig appConfig,
          @InjectService("SerialReader") SerialReader serialReader) throws Exception {
    this.portName = appConfig.getSerialPortName();
    this.serialReader = serialReader;
    populateList();
    setParametersAndConnect(portName);
  }

  private void populateList() {

    Enumeration pList = CommPortIdentifier.getPortIdentifiers();
    portsIDMap = new HashMap<>();
    // Process the list of ports, putting serial ports into ComboBox
    while (pList.hasMoreElements()) {
      CommPortIdentifier cpi = (CommPortIdentifier) pList.nextElement();
      if (cpi.getPortType() == CommPortIdentifier.PORT_SERIAL) {
        portsIDMap.put(cpi.getName(), cpi);
      }
    }
  }
  
  public void setParameterAndReconnect(String portName) {
    disconnect();
    populateList();
    try {
      setParametersAndConnect(portName);
    } catch (Exception e) {
      log.error("Error while traing reconnect the device", e);
    }
  }

  public void setParametersAndConnect(String commPortName) throws Exception {
    try {
      CommPortIdentifier cpi = (CommPortIdentifier) portsIDMap.get(commPortName);
      if (cpi == null) {
        log.error("There is no such port {}", commPortName);
        throw new ConfigurationException("Invalid port name.");
      }
      serialPort = (SerialPort) cpi.open("RS232Port", 3000);
      serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

      serialReader.setup(this);
      serialPort.addEventListener(serialReader);
      serialPort.notifyOnDataAvailable(true);
      serialPort.notifyOnBreakInterrupt(true);
      serialPort.notifyOnCarrierDetect(true);

      connected = true;
    } catch (PortInUseException e) {
      log.error("Port in use: close other app, or use different port.", e);
      connected = false;
    } catch (UnsupportedCommOperationException e) {
      log.error("Unsupported options error: try different settings.", e);
      connected = false;
    } catch (IOException e) {
      log.error("Exeption while trying gets input and/or output stream from serial port.", e);
      disconnect();
    }
  }

  public void disconnect() {

    if (serialPort != null) {
      serialPort.close();
    }
    connected = false;
  }
}
